/**
 * 工作流布局类
 *
 * API
 * ---------------------------
 * setIgnoreDelete(ignoreDelete)
 * setMergeSplitAndJoin(merge)
 * calculateLocation(process)
 *
 */

 (function(global){

     'use strict';

     const logger = WorkflowUtil.newLogger("WorkflowApp/AutoLayout");


     /**
      * LayoutRecorder API
      * -----------------------------------
      * pushSplit(splitNode)
      * popSplit()
      * getSplitSubMaxX(splitId)
      * setSplitSubMaxX(x)
      * changeCurrentSplit()
      * getTempSplitSize()
      * addSplipt2JoinRelation(splitId, joinId)
      * getSplitByJoin(joinId)
      * getJoinBySplit(splitId)
      */
     const LayoutRecorder = function () {

         this.splits = [];
         this.split2Join = {};
         this.join2Split = {};

         this.currentSplit = "";
         this.splitSubMaxX = {};

         this.pushSplit = function (splitNode) {
             this.currentSplit = splitNode.getId();
             this.splits.push(splitNode);
         };

         this.popSplit = function () {

             let lastSplit = null;
             if (this.splits.length > 0) {
                 lastSplit = this.splits.pop();
             }
             return lastSplit;
         };

         this.getSplitSubMaxX = function (splitId) {
             let ret = 0;
             const subMaxX = this.splitSubMaxX[splitId];
             if (subMaxX) {
                 ret = subMaxX;
             }
             return ret;
         };

         this.setSplitSubMaxX = function (x) {
             if (this.currentSplit) {
                 this.splitSubMaxX[this.currentSplit] = x;
             }
         };

         this.changeCurrentSplit = function () {

             if (this.currentSplit) {
                 delete this.splitSubMaxX[this.currentSplit];
             }

             if (this.splits.length > 0) {
                 this.currentSplit = this.splits[this.splits.length - 1].getId();
             } else {
                 this.currentSplit = "";
             }
         };

         this.getTempSplitSize = function () {
             return this.splits.length;
         };

         this.addSplipt2JoinRelation = function (splitId, joinId) {
             this.split2Join[splitId] = joinId;
             this.join2Split[joinId] = splitId;
         };

         this.getSplitByJoin = function (joinId) {
             return this.join2Split[joinId];
         };

         this.getJoinBySplit = function (splitId) {
             return this.split2Join[splitId];
         }
     };

     let AutoLayout = function (ignoreDelete, merge) {

         // 忽略节点删除（画出整个图）
         this.ignoreDelete = ignoreDelete === true;

         // 合并单线的join或split节点
         this.merge = merge === true;
     };

     /**
     * 
     * 
     * @param ignoreDelete
     * 
     * @Since A8-V5 6.1SP1
     * @Author : xuqw
     * @Date : 2017年12月8日上午1:20:03
     * 
     */
    AutoLayout.prototype.setIgnoreDelete = function(ignoreDelete) {
        this.ignoreDelete = ignoreDelete;
    };

    /**
     * 
     * 
     * @param merge
     * 
     * @Since A8-V5 6.1SP1
     * @Author : xuqw
     * @Date : 2017年12月8日上午1:21:02
     * 
     */
    AutoLayout.prototype.setMergeSplitAndJoin = function(merge) {
        this.merge = merge;
    };

    /**
     * 
     * 计算流程节点位置
     * 
     * @param process
     * 
     * @Since A8-V5 6.1SP1
     * @Author : xuqw
     * @Date : 2017年11月28日下午11:14:41
     * 
     */
    AutoLayout.prototype.calculateLocation = function(process) {

        const start = process.getStartNode();

        const recorder = new LayoutRecorder();

        // 计算子节点的个数
        let startTime = new Date().getTime();
        this._countSubSize(start, process, recorder);
        logger.debug("计算子节点数量耗时：" + ((new Date().getTime()) - startTime));

        const startX = 0;
        const startY = (start.getSubSize() - 1) / 2.0;

        // 计算各个节点的相对位置
        startTime = new Date().getTime();
        this._calculateNode(start, startX, startY, process, recorder);
        logger.debug("计算子节点坐标耗时：" + ((new Date().getTime()) - startTime));

        logger.debug("完成计算节点坐标");
    };

    /**
     * 计算子节点数量
     * 
     * @Since A8-V5 6.1SP1
     * @Author : xuqw
     * @Date : 2017年12月4日下午11:08:19
     * 
     */
    AutoLayout.prototype._countSubSize = function(node, process, recorder) {

        if (node.isDelete()) {
            node.setVirtual(true);
        }

        const beginCurrentSplitSize = recorder.getTempSplitSize();
        let size = 0;

        const downLinks = node.getDownLinks();
        if (downLinks && downLinks.length > 0) {

            let i = 0, subNodesLength = downLinks.length;
            for (; i < subNodesLength; i++) {

                const downLink = downLinks[i];
                const subNode = downLink.getToNode();
                if (subNode.isDelete() && !this.ignoreDelete) {
                    continue;
                }

                if(node.isSplit()){
                    // 设置第一条线和最后一条线
                    node.setFirstLineId(downLink.getId());
                    node.setLastLineId(downLink.getId());
                }
                
                node.addNextLineSize();
                subNode.setDelete(node.isDelete());

                if (subNode.isSplit()) {

                    recorder.pushSplit(subNode);
                    size += this._countSubSize(subNode, process, recorder);

                } else if (subNode.isJoin()) {

                 // 设置第一条线和最后一条线
                    subNode.setFirstLineId(downLink.getId());
                    subNode.setLastLineId(downLink.getId());

                    let splitId = recorder.getSplitByJoin(subNode.getId());
                    if (!splitId) {
                        const split = recorder.popSplit();
                        recorder.addSplipt2JoinRelation(split.getId(), subNode.getId());
                    }
                    subNode.addPreLineSize();
                } else {
                    // 普通节点
                    size += this._countSubSize(subNode, process, recorder);
                }
            }
        }

        if (size > 0) {
            node.setNextSubSize(size);
        }

        if (node.isSplit()) {

            const endCurrentSplitSize = recorder.getTempSplitSize();
            if (beginCurrentSplitSize === endCurrentSplitSize) {
                // 线断了，没有遇到join节点, 移除一下一个空的split
                recorder.popSplit();
            } else {
                const joinId = recorder.getJoinBySplit(node.getId());

                const joinNode = process.getNode(joinId);
                joinNode.setDelete(node.isDelete());

                if (!joinNode.isDelete()) {
                    // 判断上节点是否是删除状态
                    const parents = joinNode.getUpNodes();
                    let delJoin = true, k = 0, parentsLength = parents.length;
                    for (; k < parentsLength; k++) {
                        const parent = parents[k];
                        if (!parent.isDelete()) {
                            // 有没有被删除的节点
                            delJoin = false;
                            break;
                        }
                    }

                    if (delJoin) {
                        // 上面节点全部被删除了， 场景为split节点显示， join节点不显示
                        joinNode.setDelete(true);
                    }
                }

                if (joinNode.isDelete()) {
                    joinNode.setVirtual(true);
                }

                // 计算join节点的子节点数量
                const subSize = this._countSubSize(joinNode, process, recorder);
                if (subSize > size) {
                    size = subSize;
                }
            }
        } else if (node.isJoin()) {

            const childs = node.getDownNodes();
            let delJoin = true, k = 0, childsLength = childs.length;
            for (; k < childsLength; k++) {
                const child = childs[k];
                if (!child.isDelete()) {
                    // 有没有被删除的节点
                    delJoin = false;
                    break;
                }
            }

            if (delJoin) {
                // join节点后面的节点都被删除了， 直接设置当前节点为删除状态
                node.setDelete(true);
                if (this.ignoreDelete) {
                    node.setVirtual(true);
                }
            }
        }

        if (node.getSubSize() < size) {
            node.setSubSize(size);
        }

        if (!this.ignoreDelete && this.merge) {

            if ((node.isSplit() && node.getNextLineSize() <= 1) || (node.isJoin() && node.getPreLineSize() <= 1)) {
                node.setSkip(true);
            }
        }
        // logger.info(node.getName() + "(" + node.getId() + ") - " +
        // node.getSubSize() + ", " + node.getNextSubSize());
        return node.getSubSize();
    };

    /**
     * 计算坐标
     * 
     * @param node
     * @param x
     * @param y
     * @param process
     * @param recorder
     * @return
     * 
     * @Since A8-V5 6.1SP1
     * @Author : xuqw
     * @Date : 2017年12月5日上午12:32:00
     */
    AutoLayout.prototype._calculateNode = function(node, x, y, process, recorder) {

        node.setX(x);
        node.setY(y);

        // 记录流程最大的X和最大Y
        if (x > process.getMaxX())
            process.setMaxX(x);
        if (y > process.getMaxY())
            process.setMaxY(y);

        const subNodes = node.getDownNodes();
        if (subNodes && subNodes.length > 0) {

            const subSize = node.getSubSize();
            const nextSubSize = node.getNextSubSize();

            const isNextSub = subSize > nextSubSize;

            let middle;

            if (isNextSub) {
                middle = (nextSubSize + 1) / 2.0;
            } else {
                middle = (subSize + 1) / 2.0;
            }

            let index = 0;
            let i = 0, subNodesLength = subNodes.length;
            for (; i < subNodesLength; i++) {

                const subNode = subNodes[i];
                if (subNode.isHidden()) {
                    continue;
                }

                const currentIndex = index + 1;
                index += subNode.getSubSize();

                let subX = x + 1;
                if (!this.ignoreDelete && this.merge && subNode.isSkip()) {
                    subX = x;
                }
                const subY = ((y + (currentIndex - middle)) + (y + (index - middle))) / 2;

                if (subNode.isJoin()) {
                    // JOIN节点等split， join 节点的x放到后面来算
                    subNode.setY(y);
                } else {

                    if (subNode.isSplit()) {
                        recorder.pushSplit(subNode);
                    }

                    // 记录当前split节点子节点最远的X坐标
                    recorder.setSplitSubMaxX(subX);

                    // 普通节点
                    this._calculateNode(subNode, subX, subY, process, recorder);
                }
            }
        }

        if (node.isSplit()) {

            const join = process.getNode(recorder.getJoinBySplit(node.getId()));
            const splitSubMaxX = recorder.getSplitSubMaxX(node.getId());

            recorder.popSplit();
            recorder.changeCurrentSplit();
            recorder.setSplitSubMaxX(splitSubMaxX);

            if (join) {

                // join 为 null 是断线情况， split节点直接当做普通节点就好了
                let joinX = 0;
                let joinY = join.getY();

                const parentNodes = join.getUpNodes();
                let parentSize = 0;
                let k = 0, parentNodesLength = parentNodes.length;
                for (; k < parentNodesLength; k++) {
                    const bpmNode = parentNodes[k];
                    if (!bpmNode.isHidden()) {
                        parentSize++;
                    }
                    joinX = Math.max(joinX, bpmNode.getX());
                }

                if (parentSize > 1) {
                    joinY = node.getY();
                }

                // 断线的情况下最远X计算只能通过中间记录方式来获取
                joinX = Math.max(splitSubMaxX, joinX);

                if (!join.isSkip()) {
                    joinX = joinX + 1;
                }

                this._calculateNode(join, joinX, joinY, process, recorder);
            }
        }

        // logger.info(node.getName() + "(" + node.getId() + ") - " +
        // node.getX() + ", " + node.getY());
    };

    global.AutoLayout = AutoLayout;
     
 })(WorkflowApp);
